package com.djf.djfcs.esService;

import com.djf.djfcs.entity.es.ProductDocument;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 商品Elasticsearch服务
 * 提供商品文档的增删改查功能
 */
@Slf4j
@Service
public class ProductEsService {

    private final RestHighLevelClient esClient;
    private final ObjectMapper objectMapper;
    private static final String INDEX_NAME = "products";

    @Autowired
    public ProductEsService(RestHighLevelClient esClient, ObjectMapper objectMapper) {
        this.esClient = esClient;
        this.objectMapper = objectMapper;
    }

    /**
     * 创建商品索引及映射
     * @return 是否创建成功
     * @throws IOException IO异常
     */
    public boolean createProductIndex() throws IOException {
        // 检查索引是否存在
        GetIndexRequest request = new GetIndexRequest(INDEX_NAME);
        boolean exists = esClient.indices().exists(request, RequestOptions.DEFAULT);
        if (exists) {
            log.info("商品索引 '{}' 已存在", INDEX_NAME);
            return false;
        }

        // 构建创建索引请求
        CreateIndexRequest createRequest = new CreateIndexRequest(INDEX_NAME);
        createRequest.settings(Settings.builder()
                .put("index.number_of_shards", 1)
                .put("index.number_of_replicas", 0)
        );

        // 定义商品索引映射
        createRequest.mapping(
                "{\n" +
                        "  \"properties\": {\n" +
                        "    \"id\": { \"type\": \"long\" },\n" +
                        "    \"productCode\": { \"type\": \"keyword\" },\n" +
                        "    \"productName\": { \"type\": \"text\", \"analyzer\": \"ik_max_word\", \"fields\": { \"keyword\": { \"type\": \"keyword\" } } },\n" +
                        "    \"category\": { \"type\": \"keyword\" },\n" +
                        "    \"brand\": { \"type\": \"keyword\" },\n" +
                        "    \"price\": { \"type\": \"double\" },\n" +
                        "    \"stock\": { \"type\": \"integer\" },\n" +
                        "    \"status\": { \"type\": \"keyword\" },\n" +
                        "    \"description\": { \"type\": \"text\", \"analyzer\": \"ik_max_word\" },\n" +
                        "    \"imageUrl\": { \"type\": \"keyword\" },\n" +
                        "    \"tenantId\": { \"type\": \"keyword\" },\n" +
                        "    \"createTime\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd'T'HH:mm:ss\" },\n" +
                        "    \"updateTime\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd'T'HH:mm:ss\" },\n" +
                        "    \"syncTimestamp\": { \"type\": \"long\" },\n" +
                        "    \"operationType\": { \"type\": \"keyword\" }\n" +
                        "  }\n" +
                        "}",
                XContentType.JSON
        );

        // 执行创建索引操作
        AcknowledgedResponse createIndexResponse = esClient.indices().create(createRequest, RequestOptions.DEFAULT);
        log.info("商品索引创建确认: {}", createIndexResponse.isAcknowledged());
        return createIndexResponse.isAcknowledged();
    }

    /**
     * 索引商品文档 (插入或更新)
     * @param productDocument 商品文档对象
     * @return 索引到的文档ID
     * @throws IOException IO异常
     */
    public String indexProduct(ProductDocument productDocument) throws IOException {
        // 设置同步时间戳
        if (productDocument.getSyncTimestamp() == null) {
            productDocument.setSyncTimestamp(System.currentTimeMillis());
        }

        // 构建索引请求
        IndexRequest request = new IndexRequest(INDEX_NAME)
                .id(productDocument.getId().toString())
                .source(objectMapper.writeValueAsString(productDocument), XContentType.JSON);

        // 执行索引操作
        IndexResponse response = esClient.index(request, RequestOptions.DEFAULT);
        log.info("商品文档已索引，ID: {}", response.getId());
        return response.getId();
    }

    /**
     * 根据ID获取商品文档
     * @param id 商品ID
     * @return 找到的商品文档对象，如果不存在则返回 null
     * @throws IOException IO异常
     */
    public ProductDocument getProductById(Long id) throws IOException {
        GetRequest getRequest = new GetRequest(INDEX_NAME, id.toString());
        GetResponse getResponse = esClient.get(getRequest, RequestOptions.DEFAULT);

        if (getResponse.isExists()) {
            Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
            ProductDocument productDocument = objectMapper.convertValue(sourceAsMap, ProductDocument.class);
            productDocument.setId(Long.valueOf(getResponse.getId()));
            return productDocument;
        } else {
            return null;
        }
    }

    /**
     * 根据商品编码获取商品文档
     * @param productCode 商品编码
     * @return 找到的商品文档对象，如果不存在则返回 null
     * @throws IOException IO异常
     */
    public ProductDocument getProductByCode(String productCode) throws IOException {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("productCode", productCode));
        searchSourceBuilder.size(1);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);
        if (searchResponse.getHits().getTotalHits().value > 0) {
            try {
                String sourceAsString = searchResponse.getHits().getAt(0).getSourceAsString();
                ProductDocument productDocument = objectMapper.readValue(sourceAsString, ProductDocument.class);
                productDocument.setId(Long.valueOf(searchResponse.getHits().getAt(0).getId()));
                return productDocument;
            } catch (IOException e) {
                log.error("解析商品文档失败", e);
                return null;
            }
        }
        return null;
    }

    /**
     * 搜索商品文档
     * @param keyword 关键字（商品名称、描述）
     * @param category 商品分类
     * @param brand 商品品牌
     * @param status 商品状态
     * @param minPrice 最小价格
     * @param maxPrice 最大价格
     * @param minStock 最小库存
     * @param maxStock 最大库存
     * @param from 分页起始位置
     * @param size 每页大小
     * @return 匹配的商品文档列表
     * @throws IOException IO异常
     */
    public List<ProductDocument> searchProducts(String keyword, String category, String brand, String status,
                                              BigDecimal minPrice, BigDecimal maxPrice,
                                              Integer minStock, Integer maxStock,
                                              int from, int size) throws IOException {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 关键字搜索（商品名称和描述）
        if (keyword != null && !keyword.isEmpty()) {
            boolQuery.must(QueryBuilders.multiMatchQuery(keyword, "productName", "description"));
        }

        // 分类过滤
        if (category != null && !category.isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("category", category));
        }

        // 品牌过滤
        if (brand != null && !brand.isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("brand", brand));
        }

        // 状态过滤
        if (status != null && !status.isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("status", status));
        }

        // 价格范围过滤
        if (minPrice != null || maxPrice != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price")
                    .gte(minPrice)
                    .lte(maxPrice));
        }

        // 库存范围过滤
        if (minStock != null || maxStock != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("stock")
                    .gte(minStock)
                    .lte(maxStock));
        }

        searchSourceBuilder.query(boolQuery);
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchSourceBuilder.sort("createTime", SortOrder.DESC);

        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);

        List<ProductDocument> productDocuments = new ArrayList<>();
        searchResponse.getHits().forEach(hit -> {
            try {
                ProductDocument productDocument = objectMapper.readValue(hit.getSourceAsString(), ProductDocument.class);
                productDocument.setId(Long.valueOf(hit.getId()));
                productDocuments.add(productDocument);
            } catch (IOException e) {
                log.error("解析商品文档失败", e);
            }
        });
        return productDocuments;
    }

    /**
     * 根据分类获取商品列表
     * @param category 商品分类
     * @param from 分页起始位置
     * @param size 每页大小
     * @return 商品文档列表
     * @throws IOException IO异常
     */
    public List<ProductDocument> getProductsByCategory(String category, int from, int size) throws IOException {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("category", category));
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);

        List<ProductDocument> productDocuments = new ArrayList<>();
        searchResponse.getHits().forEach(hit -> {
            try {
                ProductDocument productDocument = objectMapper.readValue(hit.getSourceAsString(), ProductDocument.class);
                productDocument.setId(Long.valueOf(hit.getId()));
                productDocuments.add(productDocument);
            } catch (IOException e) {
                log.error("解析商品文档失败", e);
            }
        });
        return productDocuments;
    }

    /**
     * 根据品牌获取商品列表
     * @param brand 商品品牌
     * @param from 分页起始位置
     * @param size 每页大小
     * @return 商品文档列表
     * @throws IOException IO异常
     */
    public List<ProductDocument> getProductsByBrand(String brand, int from, int size) throws IOException {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("brand", brand));
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);

        List<ProductDocument> productDocuments = new ArrayList<>();
        searchResponse.getHits().forEach(hit -> {
            try {
                ProductDocument productDocument = objectMapper.readValue(hit.getSourceAsString(), ProductDocument.class);
                productDocument.setId(Long.valueOf(hit.getId()));
                productDocuments.add(productDocument);
            } catch (IOException e) {
                log.error("解析商品文档失败", e);
            }
        });
        return productDocuments;
    }

    /**
     * 更新商品文档的部分字段
     * @param id 商品ID
     * @param updates 要更新的字段及其值
     * @return 更新的文档ID
     * @throws IOException IO异常
     */
    public String updateProduct(Long id, Map<String, Object> updates) throws IOException {
        // 添加更新时间戳
        updates.put("syncTimestamp", System.currentTimeMillis());
        
        UpdateRequest updateRequest = new UpdateRequest(INDEX_NAME, id.toString())
                .doc(updates, XContentType.JSON);

        UpdateResponse updateResponse = esClient.update(updateRequest, RequestOptions.DEFAULT);
        log.info("商品文档已更新，ID: {}, 结果: {}", updateResponse.getId(), updateResponse.getResult());
        return updateResponse.getId();
    }

    /**
     * 更新商品库存
     * @param id 商品ID
     * @param stock 新库存数量
     * @return 更新的文档ID
     * @throws IOException IO异常
     */
    public String updateProductStock(Long id, Integer stock) throws IOException {
        Map<String, Object> updates = new HashMap<>();
        updates.put("stock", stock);
        updates.put("updateTime", LocalDateTime.now());
        return updateProduct(id, updates);
    }

    /**
     * 更新商品价格
     * @param id 商品ID
     * @param price 新价格
     * @return 更新的文档ID
     * @throws IOException IO异常
     */
    public String updateProductPrice(Long id, BigDecimal price) throws IOException {
        Map<String, Object> updates = new HashMap<>();
        updates.put("price", price);
        updates.put("updateTime", LocalDateTime.now());
        return updateProduct(id, updates);
    }

    /**
     * 更新商品状态
     * @param id 商品ID
     * @param status 新状态
     * @return 更新的文档ID
     * @throws IOException IO异常
     */
    public String updateProductStatus(Long id, String status) throws IOException {
        Map<String, Object> updates = new HashMap<>();
        updates.put("status", status);
        updates.put("updateTime", LocalDateTime.now());
        return updateProduct(id, updates);
    }

    /**
     * 根据ID删除商品文档
     * @param id 商品ID
     * @return 是否删除成功
     * @throws IOException IO异常
     */
    public boolean deleteProduct(Long id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, id.toString());
        DeleteResponse deleteResponse = esClient.delete(deleteRequest, RequestOptions.DEFAULT);
        log.info("商品文档已删除，ID: {}, 结果: {}", deleteResponse.getId(), deleteResponse.getResult());
        return deleteResponse.getResult().equals(DeleteResponse.Result.DELETED);
    }

    /**
     * 批量索引商品文档
     * @param productDocuments 商品文档列表
     * @return 成功索引的文档数量
     * @throws IOException IO异常
     */
    public int batchIndexProducts(List<ProductDocument> productDocuments) throws IOException {
        int successCount = 0;
        for (ProductDocument productDocument : productDocuments) {
            try {
                indexProduct(productDocument);
                successCount++;
            } catch (IOException e) {
                log.error("批量索引商品失败，商品ID: {}", productDocument.getId(), e);
            }
        }
        log.info("批量索引商品完成，成功: {}, 总数: {}", successCount, productDocuments.size());
        return successCount;
    }

    /**
     * 删除商品索引
     * @return 是否删除成功
     * @throws IOException IO异常
     */
    public boolean deleteProductIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest(INDEX_NAME);
        AcknowledgedResponse deleteResponse = esClient.indices().delete(request, RequestOptions.DEFAULT);
        log.info("商品索引删除确认: {}", deleteResponse.isAcknowledged());
        return deleteResponse.isAcknowledged();
    }
}